package simulation;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;

public class Raycaster {
	public static int cast(Maze maze, Location location, int rotation) {
		Location start = location;
		Coordinate startsector = new Coordinate((int)Math.floor(start.x/Maze.SECTOR_WIDTH),(int)Math.floor(start.y/Maze.SECTOR_WIDTH));
		if(startsector.x>maze.getMaxX() || startsector.y>maze.getMaxY() || startsector.x<maze.getMinX() || startsector.y<maze.getMinY()) return 0;//we're outside the maze (the fog of war is too thick to see anything)
		double rotRad = Math.toRadians(rotation);
		double rotCos = Math.cos(rotRad);
		if(((rotation+90)%360)==0) rotCos = 1.0/999999999999.9;
		double rotSin = Math.sin(rotRad);
		if((rotation%360)==0) rotSin = 1.0/999999999999.9;
		Location end = new Location(0,0); //let op: end.x en end.y horen niet bij elkaar
		Coordinate endsector = new Coordinate(0,0); // same here
		Direction wallx = Direction.EAST;
		Direction wally = Direction.NORTH;
		
		//shortest x
		endsector.x = startsector.x + ((rotCos<0.0)?0:1); //take the wall left or right depending on the angle
		if(rotCos<0.0) wallx = Direction.WEST;
		double diff = endsector.x*Maze.SECTOR_WIDTH - start.x;
		double distx = diff/rotCos; //de afstand die gereden moet worden om mogelijk een verticale muur te overschrijden
		end.y = start.y + distx*rotSin; //y-coordinaat van de intersectie met die verticale muur
		if(((rotation+90)%180)==0) distx = 99999999999999.9;

		//shortest y
		endsector.y = startsector.y + ((rotSin<0.0)?0:1); //take the wall up or down depending on the angle
		if(rotSin<0.0) wally = Direction.SOUTH;
		diff = endsector.y*Maze.SECTOR_WIDTH - start.y;
		double disty = diff/rotSin; //de afstand die gereden moet worden om mogelijk een horizontale muur te overschrijden
		end.x = start.x + disty*rotCos; //x-coordinaat van de intersectie met die horizontale muur
		if((rotation%180)==0) disty = 99999999999999.9;
		
		if(Math.abs(distx-disty)<0.001) {
			//randgeval waarbij de trace bijna exact door hoek gaat (valt normaal niet voor met echte data uit de simulator, maar never tempt the odds)
			if(distx<disty) {
				boolean wall = (maze.read(startsector.x, startsector.y, wallx, true)!=1) && (maze.read(startsector.x+ Direction.xdelta(wallx), startsector.y+Direction.ydelta(wally), wally.opposite(), true)!=1);
				return (int)Math.round(distx + (wall?(double)(cast(maze, new Location(endsector.x*Maze.SECTOR_WIDTH+(0.001*rotCos),end.y+(0.001*rotSin)),rotation)+0.001):0.0));
			} else {
				boolean wall = (maze.read(startsector.y, startsector.y, wally, true)!=1) && (maze.read(startsector.x+Direction.xdelta(wallx), startsector.y+Direction.ydelta(wally), wallx.opposite(), true)!=1);
				return (int)Math.round(disty + (wall?(double)(cast(maze, new Location(end.x+(0.001*rotCos),endsector.y*Maze.SECTOR_WIDTH+(0.001*rotSin)),rotation)+0.001):0.0));
			}
		}
		if(distx<disty) { //we rijden over een verticale muur voor we over een horizontale rijden
			//geef de afstand tot de mogelijke muur en als er geen muur echt staat, de afstand van daar uit
			boolean wall = maze.read(startsector.x, startsector.y, wallx, true)!=1;
			//ga 0.001cm verder (nodig om bij een negatieve richting in de juiste sector te starten)
			return (int)Math.floor(distx + (wall?(double)(cast(maze, new Location(endsector.x*Maze.SECTOR_WIDTH+(0.001*rotCos),end.y+(0.001*rotSin)),rotation)+0.001):0.0));
		} else {
			boolean wall = maze.read(startsector.x, startsector.y, wally, true)!=1;
			return (int)Math.floor(disty + (wall?(double)(cast(maze, new Location(end.x+(0.001*rotCos),endsector.y*Maze.SECTOR_WIDTH+(0.001*rotSin)),rotation)+0.001):0.0));
		}
	}
}
